package com.asdhawaii.otello;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JApplet;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import com.asdhawaii.otello.board.SquareContents;
import com.asdhawaii.otello.players.AbstractOtelloAi;
import com.asdhawaii.otello.players.Level0Ai;
import com.asdhawaii.otello.players.Level1Ai;
import com.asdhawaii.otello.players.PlayerType;

/**
 * The GUI for the Otello application.
 * 
 * To avoid copyright issues, the name of this application is "Otello" rather than "Othello;" 
 * the former refers to the Verdi opera based on the Shakespeare play.
 * 
 * @author Branden Ogata
 *
 */

@SuppressWarnings("serial")
public class Otello extends JApplet
{
  /**
   * The frame that the application will run in.
   * 
   */
  private JFrame frame;
  
  /**
   * The menu bar for the application.
   * 
   */
  private JMenuBar menuBar;
  
  /**
   * The File menu.
   * 
   */
  private JMenu file;
  
  /**
   * The New Game menu item.
   * 
   */
  private JMenuItem newGame;
  
  /**
   * The Exit menu item.
   * 
   */
  private JMenuItem exit;
  
  /**
   * The Options menu.
   * 
   */
  private JMenu options;
  
  /**
   * The Help menu.
   * 
   */
  private JMenu help;
  
  /**
   * The Rules of the Game item.
   * 
   */
  private JMenuItem rules;
  
  /**
   * The About item.
   * 
   */
  private JMenuItem about;
  
  /** 
   * The display area.
   * 
   */
  private OtelloPanel display;

  /**
   * Creates a new Otello instance.
   * 
   */
  
  public Otello()
  {
    start();
  }
  
  /**
   * Initializes the game as an application.
   * 
   * @param args The String[] containing command line arguments, which are not used here.
   * 
   */
  
  public static void main(String[] args)
  {
    new Otello();
  }
  
  /**
   * Initializes the game as an applet.
   * 
   */
  
  @Override
  public final void start()
  {
    this.frame = new JFrame("Othello");
    this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.menuBar = new JMenuBar();
    
    MenuListener listener = new MenuListener();
    
    // The File menu
    this.file = new JMenu("File");
    this.newGame = new JMenuItem("New Game");
    this.newGame.addActionListener(listener);
    this.file.add(this.newGame);
    this.exit = new JMenuItem("Exit");
    this.exit.addActionListener(listener);
    this.file.add(this.exit);
    this.menuBar.add(this.file);
    
    // The Options menu
    this.options = new JMenu("Options");
    this.menuBar.add(this.options);
    
    // The Help menu
    this.help = new JMenu("Help");
    this.rules = new JMenuItem("Rules of the Game");
    this.rules.addActionListener(listener);
    this.help.add(this.rules);
    this.about = new JMenuItem("About");
    this.about.addActionListener(listener);
    this.help.add(this.about);
    this.menuBar.add(this.help);
    
    // Add the menu bar to the frame
    this.frame.setJMenuBar(this.menuBar);
    
    // The board
    this.display = new OtelloPanel();
    this.frame.getContentPane().add(this.display, BorderLayout.CENTER);
    
    // Display the frame
    this.frame.pack();
    this.frame.setVisible(true);
    this.setVisible(true);
  }
  
  /**
   * Listens for any menu selections.
   * 
   */
  
  private class MenuListener implements ActionListener
  {
    /**
     * Handles any menu selections that do occur.
     * 
     * @param event The ActionEvent that recorded the menu selection.
     * 
     */
    
    @Override
    public void actionPerformed(ActionEvent event)
    {
      // New Game should show a dialog offering options for starting a new game
      if (event.getSource() == newGame)
      {
        // The overall panel for the dialog
        JPanel newGameInfo = new JPanel();

        // The panel containing the input for Player 0 (Black)
        JPanel playerZeroInfo = new JPanel();
        playerZeroInfo.setLayout(new BoxLayout(playerZeroInfo, BoxLayout.PAGE_AXIS));
        playerZeroInfo.setBorder(BorderFactory.createTitledBorder("Black"));
        playerZeroInfo.setAlignmentX(0);

        // The name for Player 0
        JPanel playerZeroNamePanel = new JPanel();
        JLabel playerZeroNameLabel = new JLabel("Name");
        playerZeroNamePanel.add(playerZeroNameLabel);
        JTextField playerZeroNameTextField = new JTextField(display.getPlayerZeroName(), 16);
        playerZeroNamePanel.add(playerZeroNameTextField);
        playerZeroInfo.add(playerZeroNamePanel);
        
        // The type for Player 0
        JPanel playerZeroTypePanel = new JPanel();
        playerZeroTypePanel.setLayout(new BoxLayout(playerZeroTypePanel, BoxLayout.PAGE_AXIS));
        playerZeroTypePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
        JLabel playerZeroTypeChoiceLabel = new JLabel("Player Type");
        playerZeroTypePanel.add(playerZeroTypeChoiceLabel);
        playerZeroInfo.add(playerZeroTypePanel);
        ButtonGroup playerZeroTypeChoices = new ButtonGroup();
        JRadioButton playerZeroGirlButton = new JRadioButton("Girl");
        playerZeroGirlButton.setActionCommand(playerZeroGirlButton.getText());
        JRadioButton playerZeroBoyButton = new JRadioButton("Boy");
        playerZeroBoyButton.setActionCommand(playerZeroBoyButton.getText());        
        JRadioButton playerZeroComputer0Button = new JRadioButton("Computer: Level 0");
        playerZeroComputer0Button.setActionCommand(playerZeroComputer0Button.getText());        
        JRadioButton playerZeroComputer1Button = new JRadioButton("Computer: Level 1");
        playerZeroComputer1Button.setActionCommand(playerZeroComputer1Button.getText());        
        playerZeroTypeChoices.add(playerZeroGirlButton);
        playerZeroTypeChoices.add(playerZeroBoyButton);
        playerZeroTypeChoices.add(playerZeroComputer0Button);
        playerZeroTypeChoices.add(playerZeroComputer1Button);
        playerZeroTypePanel.add(playerZeroGirlButton);
        playerZeroTypePanel.add(playerZeroBoyButton);
        playerZeroTypePanel.add(playerZeroComputer0Button);
        playerZeroTypePanel.add(playerZeroComputer1Button);
        
        // Select the initial type for Player 0; by default, should be GIRL (Christine)
        switch (display.getPlayerZeroType())
        {
          case COMPUTER:
            AbstractOtelloAi currentAi = display.getPlayerZeroAi();
            
            if (currentAi instanceof Level0Ai)
            {
              playerZeroComputer0Button.setSelected(true);
            }
            else if (currentAi instanceof Level1Ai)
            {
              playerZeroComputer1Button.setSelected(true);              
            }
            
            break;
          case BOY:
            playerZeroBoyButton.setSelected(true);
            break;
          case GIRL:
          default:
            playerZeroGirlButton.setSelected(true);
            break;
        }
        
        newGameInfo.add(playerZeroInfo);
        
        // The panel containing the input for Player 1 (White)
        JPanel playerOneInfo = new JPanel();
        playerOneInfo.setLayout(new BoxLayout(playerOneInfo, BoxLayout.PAGE_AXIS));
        playerOneInfo.setBorder(BorderFactory.createTitledBorder("White"));
        playerOneInfo.setAlignmentX(0);

        // The name for Player 1
        JPanel playerOneNamePanel = new JPanel();
        JLabel playerOneNameLabel = new JLabel("Name");
        playerOneNamePanel.add(playerOneNameLabel);
        JTextField playerOneNameTextField = new JTextField(display.getPlayerOneName(), 16);
        playerOneNamePanel.add(playerOneNameTextField);
        playerOneInfo.add(playerOneNamePanel);

        // The type for Player 1
        JPanel playerOneTypePanel = new JPanel();
        playerOneTypePanel.setLayout(new BoxLayout(playerOneTypePanel, BoxLayout.PAGE_AXIS));
        playerOneTypePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
        JLabel playerOneTypeChoiceLabel = new JLabel("Player Type");
        playerOneTypePanel.add(playerOneTypeChoiceLabel);
        playerOneInfo.add(playerOneTypePanel);
        ButtonGroup playerOneTypeChoices = new ButtonGroup();
        JRadioButton playerOneGirlButton = new JRadioButton("Girl");
        playerOneGirlButton.setActionCommand(playerOneGirlButton.getText());
        JRadioButton playerOneBoyButton = new JRadioButton("Boy");
        playerOneBoyButton.setActionCommand(playerOneBoyButton.getText());        
        JRadioButton playerOneComputer0Button = new JRadioButton("Computer: Level 0");
        playerOneComputer0Button.setActionCommand(playerOneComputer0Button.getText());        
        JRadioButton playerOneComputer1Button = new JRadioButton("Computer: Level 1");
        playerOneComputer1Button.setActionCommand(playerOneComputer1Button.getText());        
        playerOneTypeChoices.add(playerOneGirlButton);
        playerOneTypeChoices.add(playerOneBoyButton);
        playerOneTypeChoices.add(playerOneComputer0Button);
        playerOneTypeChoices.add(playerOneComputer1Button);
        playerOneTypePanel.add(playerOneGirlButton);
        playerOneTypePanel.add(playerOneBoyButton);
        playerOneTypePanel.add(playerOneComputer0Button);
        playerOneTypePanel.add(playerOneComputer1Button);
        
        // Select the initial type for Player 1; by default, should be BOY (Branden)
        switch (display.getPlayerOneType())
        {
          case COMPUTER:
            AbstractOtelloAi currentAi = display.getPlayerOneAi();
            
            if (currentAi instanceof Level0Ai)
            {
              playerOneComputer0Button.setSelected(true);
            }
            else if (currentAi instanceof Level1Ai)
            {
              playerOneComputer1Button.setSelected(true);              
            }
            
            break;
          case GIRL:
            playerOneGirlButton.setSelected(true);
            break;
          case BOY:
          default:
            playerOneBoyButton.setSelected(true);
            break;
        }
        
        newGameInfo.add(playerOneInfo);
        
        // Show the dialog and store the result
        int result = JOptionPane.showConfirmDialog(null, newGameInfo, "New Game", 
                                                   JOptionPane.YES_NO_OPTION);
        
        // If the user chose to start a new game, 
        // then create a new game with the specified values
        // (Note that Computer must be added as a player type when AI players are implemented)
        if (result == JOptionPane.YES_OPTION)
        {
          display.setPlayerZeroName(playerZeroNameTextField.getText());
          display.setPlayerZeroType(getSelectedType(playerZeroTypeChoices));
          display.setPlayerZeroAi(getSelectedAi(playerZeroTypeChoices, SquareContents.BLACK));
          
          display.setPlayerOneName(playerOneNameTextField.getText());
          display.setPlayerOneType(getSelectedType(playerOneTypeChoices));
          display.setPlayerOneAi(getSelectedAi(playerOneTypeChoices, SquareContents.WHITE));
          
          display.reset();
        }
      }
      
      // Exit should close the frame
      else if (event.getSource() == exit)
      {
        frame.dispose();
      }
      
      // Rules of the Game should show a JOptionPane describing how to play Otello
      else if (event.getSource() == rules)
      {
        final String WHITE_PIECE = "\u25CB";
        final String BLACK_PIECE = "\u25CF";
        String info = "";
        info += "Overview\n";
        info += "Otello (better known as Othello\u00A9 (better known as Reversi))\n";
        info += "is a board game in which you attempt to end the game with as many\n";
        info += "pieces of your color on the board as possible.  To accomplish this,\n";
        info += "you can flip opposing pieces if they are between one of your pieces\n";
        info += "and a piece that you place on that turn.\n";
        info += "\n";
        info += "Rules\n";
        info += "Players take alternating turns placing pieces of their color on the\n";
        info += "board.  Each turn involves placing a piece horizontally, vertically,\n";
        info += "or diagonally collinear with one of your pieces with at least one\n";
        info += "opposing piece separating your pieces, then flipping those opposing\n"; 
        info += "pieces to your color.  For example, given the arrangement of pieces below:\n";
        info += "\n";
        info += BLACK_PIECE + WHITE_PIECE + WHITE_PIECE + "\n";
        info += "\n";
        info += "Black can place a piece at the right of the row as shown below:\n";
        info += "\n";
        info += BLACK_PIECE + WHITE_PIECE + WHITE_PIECE + BLACK_PIECE + "\n";
        info += "This will flip the white pieces to black.\n";
        info += "\n";
        info += BLACK_PIECE + BLACK_PIECE + BLACK_PIECE + BLACK_PIECE + "\n";
        info += "\n";
        info += "How to Play\n";
        info += "To place a piece on the board, click on the desired square.  If the move\n";
        info += "is valid, this will place the piece of your color on the board in that\n";
        info += "square.  Otherwise, the piece will not appear and you will have to\n";
        info += "select another square.\n";
        info += "The table on the right-hand side of the window provides information\n";
        info += "about the two players and how many pieces each player has.  The player\n";
        info += "highlighted in blue is the player who will move next.\n";
        
        JOptionPane.showMessageDialog(null, info);        
        
      }
      
      // About should provide information about the developers and the application
      else if (event.getSource() == about)
      {
        String info = "";
        info += "Otello\n";
        info += "\n";
        info += "Developer: Branden Ogata\n";
        info += "Artist: Samantha Yuki\n";
        info += "\n";
        info += "Copyright 2013 A Sense of DESIGN\n";
        
        JOptionPane.showMessageDialog(null, info);        
      }
    }
    
    /**
     * Returns the type corresponding to the selected player.
     * 
     * @param buttons The ButtonGroup containing the options for player selection.
     * 
     * @return type A PlayerType corresponding to the user selection;
     *              null if no match can be found.
     *         
     */
    
    private PlayerType getSelectedType(ButtonGroup buttons)
    {
      PlayerType type = null;
      
      String selected = buttons.getSelection().getActionCommand().toLowerCase();
      
      if ("girl".equals(selected))
      {
        type = PlayerType.GIRL;
      }
      else if ("boy".equals(selected))
      {
        type = PlayerType.BOY;
      }
      else if (selected.startsWith("computer"))
      {
        type = PlayerType.COMPUTER;
      }
      // Need some error case here (and corresponding graphics)
      
      return type;
    }
    
    /**
     * Returns the AI corresponding to the selected player.
     * 
     * @param buttons The ButtonGroup containing the options for player selection.
     * @param color The SquareContents indicating the color to assign to the AI if any.
     * 
     * @return ai An AbstractOtelloAi corresponding to the user selection;
     *            null if the selection is not an AI.
     *            Also has nothing to do with love.
     *            
     */
    
    private AbstractOtelloAi getSelectedAi(ButtonGroup buttons, SquareContents color)
    {
      AbstractOtelloAi ai = null;
      
      String selected = buttons.getSelection().getActionCommand().toLowerCase();
      
      if (selected.contains("level 0"))
      {
        ai = new Level0Ai(color);
      }
      else if (selected.contains("level 1"))
      {
        ai = new Level1Ai(color);
      }
      
      return ai;
    }
  }
}
